/*
 * IndexEditor.cpp
 *
 *  Created on: 30-Oct-2013
 *      Author: sriram
 */

#include "IndexEditor.h"
#include "../dependency/debug.h"
#include "../dependency/StatusCodes.h"
#include "../dependency/GlobalDefines.h"
#include "../IndexManager/IndexHeaderPage.h"
#include "../BufferManager/BufferManager.h"
#include "../IndexManager/BPlusTree.h"
#include "../IndexManager/BPlusTreeLeafNode.h"
#include "../Utils/Timer.h"
#include "../PageLayout/Page.h"
#include "../parser/ParseUtil.h"
#include "../sql/QueryDispatcher.h"
#include "../IndexManager/IndexUtil.h"
#include <iostream>
#include <string>
#include <sstream>

IndexEditor::IndexEditor() {
    // TODO Auto-generated constructor stub
    useIndexName = "";
}

IndexEditor::~IndexEditor() {
    // TODO Auto-generated destructor stub
}

void IndexEditor::showEditor() {

    //		BufferManager *buffMgr = BufferManager::getInstance();
    //        if(bm->isDBOpen == true){
    //            bm->closeDB();        
    //        }
    //		buffMgr->useDB("hello");
    string query;
    while (true) {
        query = "";
        cout << endl << "Index# ";
        do {
            string subq;
            cin >> subq;
            if (query.length() > 0)
                query.append(" ");
            query.append(subq);

        } while (query[query.length() - 1] != ';');

        DEBUG("QUERY:" << query);

        if (query.compare("EXIT;") == 0
                || query.compare("EXIT ;") == 0) {
            cout << endl << "exiting Index Manager, bye!" << endl;
            break;
        } else {
            query = query.substr(0, query.size() - 1);
            Timer start;
            start.startTimer();
            int err = execute(query);
            if (err != SUCCESS) {
                cout << "\nSorry Error Occurred :";
            } else {
                start.endTimer();
            }
        }
    }
    //		buffMgr->closeDB();
}

int IndexEditor::createIndex(string indexName, int fanout) {
    IndexHeaderPage indexHeaderPage;
    int colType[] = {TYPE_INTEGER};
    int colSize[] = {sizeof (int)};
    indexHeaderPage.createIndexHeaderPage(1, fanout, colType, colSize, sizeof (int));
    return indexHeaderPage.getPageNumber();
}

void generateKey(char *&keyData, vector<string> literalList, int indexHeaderPage) {    
    IndexHeaderPage indexHeader(indexHeaderPage);
    int *columnTypes = indexHeader.getColTypes();
    int *columnSizes = indexHeader.getColSizes();
    int offset = 0;
    for (unsigned i = 0; i < literalList.size(); i++) {
        //int fieldNum = _schema.getColumnNum(columnList[i]);
        
        cout<<literalList[i]<<": "<<columnTypes[i]<<endl;
        if (columnTypes[i] == TYPE_INTEGER) {
            int recordData = Util::string_to_int(literalList[i]);
            DEBUG("keyData" << recordData);
            memcpy(&keyData[offset], &recordData, columnSizes[i]);
            offset = offset + columnSizes[i];
        } else if (columnTypes[i] == TYPE_FLOAT) {
            float recordData = Util::string_to_float(literalList[i]);
            DEBUG("keyData" << recordData);
            memcpy(&keyData[offset], &recordData, columnSizes[i]);
            offset = offset + columnSizes[i];
        } else if (columnTypes[i] == TYPE_VARCHAR) {
            cout<<"Came here"<<endl;
            const char *recordData = literalList[i].c_str();
            cout<<recordData<<": "<<literalList[i];
            memcpy(&keyData[offset], recordData, columnSizes[i]);            
            offset = offset + columnSizes[i];
        }
        cout<<columnSizes[i];
    }
}

void IndexEditor::insertIndex(string indexName, char *key) {
    IndexHeaderPage indexPage(hashIndexPage[indexName]);
    // cout<<indexPage.toString();
    BPlusTree bTree(hashIndexPage[indexName]);
    RIDStruct rid;
    int keyInt;
    memcpy(&keyInt, key, sizeof (int));
    rid.pageNumber = keyInt; // dummy values
    rid.slotNumber = keyInt;
    bTree.insert(key, rid);

}

void IndexEditor::display(string indexName) {
    //    if(rootPageNumber!=-1){
    //        BPlusTreeNode bRoot(indexPage.getRootPageNumber());
    //        cout<<bRoot.toString();
    //    }
}

STATUS_CODE IndexEditor::execute(string query) {
    stringstream ss(query);
    string buf;
    vector<string> tokens;
    while (ss >> buf) {
        tokens.push_back(buf);
    }

    //string command = tokens[0];
    // Checking for Create index
    if (tokens.size() == 1 && tokens[0].compare("help") == 0) {
        cout << "Note: The operations are being done on BPlusTree with " << endl;
        cout << "No of leaf records = 6" << endl;
        cout << "Fanout = 6" << endl << endl;
        //cout<<""
        cout << "1. create <index_name> <fanout>" << endl;
        cout << "8. use <index_name>" << endl;
        cout << "2. display" << endl;
        cout << "5. insert <key_value>" << endl;
        cout << "4. insert range <start_range> <end_range>" << endl;
        cout << "3. insert random <number_of_keys>" << endl;
        cout << "         Note: The key_values will be in interval [0, 199]" << endl;
        cout << "5. insert duplicate <key_value> <repeat_count>" << endl;

        cout << "6. display tree" << endl;
        cout << "7. search <key_value>" << endl;
        cout << "9. display node <page_number>" << endl;
        cout << "         Note: For internal nodes of BPlusTree" << endl;
        cout << "10. display leaf <page_number>" << endl;
        cout << "         Note: For leaf nodes of BPlusTree" << endl;
        cout << "11. delete <key_value>" << endl;
    } else if (tokens.size() == 2 && tokens[0].compare("RUN") == 0) {
        string filename = "test.sql";
        filename = tokens[1];
        string filepath = "Test/indextest/";

        filepath.append(filename);
        std::ifstream file(filepath.c_str());
        std::string query;
        while (std::getline(file, query)) { // Process str
            if (query[0] == '-' && query[1] == '-') continue;

            //string query = "CREATE DATABASE HEL;";
            //cout << "\n Runing =>" << query << endl;
            ParseUtil p;
            p.parseQuery(query); //operate 
            QueryDispatcher em;
            int err = em.run(queryStruct);
            if (err != SUCCESS) {
                cout << "\nSorry Error Occurred :" << queryStruct->errorMessage;
            }
        }

        cout << "\n *** SCRIPT EXECUTED COMPLETELY !!!";
    }// create 
    else if (tokens.size() == 1 && tokens[0].compare("LISTCATALOG") == 0) {
        string query = "SELECT * FROM SYSINDEX;";
        //cout << "\n Runing =>" << query << endl;
        ParseUtil p;
        p.parseQuery(query); //operate 
        QueryDispatcher em;
        int err = em.run(queryStruct);
        if (err != SUCCESS) {
            cout << "\nSorry Error Occurred :" << queryStruct->errorMessage;
        }
    } else if (tokens.size() == 3 && tokens[0].compare("CREATE") == 0) {
        int fanout = atoi(tokens[2].c_str());
        int pageNumber = createIndex(tokens[1], fanout);
        cout << "Index Created!!!" << endl;
        hashIndexPage[tokens[1]] = pageNumber;
        //display(tokens[2]);
        // create("1");
    }// Use Index
    else if (tokens.size() == 2 && tokens[0].compare("USE") == 0) {
        if (hashIndexPage.find(tokens[1]) == hashIndexPage.end()) {
            cout << "Index Does not exist!!! Please enter valid index!!!";
            return FAILURE;
        }
        useIndexName = tokens[1];
        cout << "Using index " << useIndexName << "!!!" << endl;
    }// Use Index
    else if (tokens.size() == 3 && tokens[0].compare("USE") == 0) {
        hashIndexPage[tokens[1]] = atoi(tokens[2].c_str());
        useIndexName = tokens[1];
        cout << "Using index " << useIndexName << "!!!" << endl;
    }// Display Index configuration
    else if (tokens.size() == 1 && tokens[0].compare("DISPLAY") == 0) {
        if (useIndexName.compare("") == 0) {
            cout << "Please Use an index to perform this operation!!!";
            return FAILURE;
        }
        IndexHeaderPage indexPage(hashIndexPage[useIndexName]);
        cout << indexPage.toString();
    }        // Inserting into index
    else if (tokens.size() == 2 && tokens[0].compare("INSERT") == 0) {
        /*
         * Considering the maxRecordsInLeaf = 6, fanout = 6
         * Set arr[] = 
         * Cases where the position of insertion in a node is at the ending itself...
         * TestCase 1: {1, 2, 3, 4 } => Only one node
         * TestCase 2: {1, 2, 3, 4, 5, 6, 7} => Split leaf to get root with only one key
         * TestCase 3: {1, 2, 3, 4, 5, 6, 7, 8, 9, 10} => Split leaf to get root with only two keys
         * TestCase 4: {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13} => Split leaf to get root with only 3 keys
         * TestCase 5: {13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1} => To check working of the insertion (comparison)...
         * 
         * TestCase 6: {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34}; => Requires to Split Root node
         */
        //        int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25};
        //        for(int i=0; i<sizeof(arr)/sizeof(int); i++){
        //            char str[10]; 
        //            memcpy(str, &arr[i], sizeof(int));
        //            int key = 0;
        //            memcpy(&key, str, sizeof(int));
        //            insertIndex(tokens[2], str);
        //        }
        if (useIndexName.compare("") == 0) {
            cout << "Please Use an index to perform this operation!!!";
            return FAILURE;
        }
        int n = atoi(tokens[1].c_str());
        char str[10];
        memcpy(str, &n, sizeof (int));
        insertIndex(useIndexName, str);
        cout << "Insertion Complete!!!" << endl;
    }// Inserting into index
    else if (tokens.size() == 5 && tokens[0].compare("INSERT") == 0 && tokens[1].compare("RANGE") == 0) {
        if (useIndexName.compare("") == 0) {
            cout << "Please Use an index to perform this operation!!!";
            return FAILURE;
        }
        int startRange = atoi(tokens[2].c_str());
        int endRange = atoi(tokens[3].c_str());
        int increment = atoi(tokens[4].c_str());
        for (int i = startRange; i <= endRange; i += increment) {
            char str[10];
            memcpy(str, &i, sizeof (int));
            insertIndex(useIndexName, str);
        }
        cout << "Insertion Complete!!!" << endl;
    }// Inserting into index
    else if (tokens.size() == 3 && tokens[0].compare("INSERT") == 0 && tokens[1].compare("RANDOM") == 0) {
        if (useIndexName.compare("") == 0) {
            cout << "Please Use an index to perform this operation!!!";
            return FAILURE;
        }
        int n = atoi(tokens[2].c_str());
        for (int i = 0; i < n; i++) {
            int randNumber = random() % 200;
            char str[10];
            memcpy(str, &randNumber, sizeof (int));
            insertIndex(useIndexName, str);

            //            IndexHeaderPage indexPage(hashIndexPage[tokens[2]]);
            //            int rootPageNumber =  indexPage.getRootPageNumber();
            //            BPlusTree bTree(indexPage.getPageNumber());
            //            bTree.levelOrderTraverse();            
        }
        cout << "Insertion Complete!!!" << endl;
    } else if (tokens.size() == 4 && tokens[0].compare("INSERT") == 0 && tokens[1].compare("DUPLICATE") == 0) {
        if (useIndexName.compare("") == 0) {
            cout << "Please Use an index to perform this operation!!!";
            return FAILURE;
        }
        int n = atoi(tokens[3].c_str());
        for (int i = 0; i < n; i++) {
            int duplicate = atoi(tokens[2].c_str());
            char str[10];
            memcpy(str, &duplicate, sizeof (int));
            insertIndex(useIndexName, str);
            //            IndexHeaderPage indexPage(hashIndexPage[useIndexName]);
            //            int rootPageNumber =  indexPage.getRootPageNumber();
            //            BPlusTree bTree(indexPage.getPageNumber());
            //            bTree.levelOrderTraverse();            
        }
        cout << "Insertion Complete!!!" << endl;
    }// Display index tree
    else if (tokens.size() == 2 && tokens[0].compare("DISPLAY") == 0 && tokens[1].compare("TREE") == 0) {
        if (useIndexName.compare("") == 0) {
            cout << "Please Use an index to perform this operation!!!";
            return FAILURE;
        }
        IndexHeaderPage indexPage(hashIndexPage[useIndexName]);
        BPlusTree bTree(indexPage.getPageNumber());
        bTree.levelOrderTraverse();
    }// Search Index
    else if (tokens.size() == 2 && tokens[0].compare("SEARCH") == 0) {
        if (useIndexName.compare("") == 0) {
            cout << "Please Use an index to perform this operation!!!";
            return FAILURE;
        }
        BPlusTree bTree(hashIndexPage[useIndexName]);
        int key = atoi(tokens[1].c_str());
        char str[10];
        memcpy(str, &key, sizeof (int));
        int foundPageNumber = bTree.search(str);
        cout << "Key found at: " << foundPageNumber << endl;
        cout << "Search Complete!!!" << endl;
    }// Search Index
    else if (tokens.size() == 2 && tokens[0].compare("SEARCH") == 0) {
        if (useIndexName.compare("") == 0) {
            cout << "Please Use an index to perform this operation!!!";
            return FAILURE;
        }
        BPlusTree bTree(hashIndexPage[useIndexName]);
        int key = atoi(tokens[1].c_str());
        char str[10];
        memcpy(str, &key, sizeof (int));
        int foundPageNumber = bTree.search(str);
        cout << "Key found at: " << foundPageNumber << endl;
        cout << "Search Complete!!!" << endl;
    }// Search Index
    else if (tokens.size() >= 3 && tokens[0].compare("SEARCHN") == 0) {
        if (useIndexName.compare("") == 0) {
            cout << "Please Use an index to perform this operation!!!";
            return FAILURE;
        }
        IndexHeaderPage indexHeader(hashIndexPage[useIndexName]);
        BPlusTree bTree(hashIndexPage[useIndexName]);
        char *str = new char[indexHeader.getKeySize()];
        vector<RIDStruct> arrRid;
        vector<int> arrOperatorType;
        vector<string> literalList;
        int i;
        for (i = 1; i < tokens.size(); i += 2) {
            if (tokens[i].compare("=") == 0) 
                arrOperatorType.push_back(EQUAL_TO);
            else if (tokens[i].compare("<") == 0)
                arrOperatorType.push_back(LESS_THAN);
            else if (tokens[i].compare("<=") == 0)
                arrOperatorType.push_back(LESS_THAN_OR_EQUAL_TO);
            else if (tokens[i].compare(">") == 0)
                arrOperatorType.push_back(GREATER_THAN);
            else if (tokens[i].compare(">=") == 0)
                arrOperatorType.push_back(GREATER_THAN_OR_EQUAL_TO);
            literalList.push_back(tokens[i+1]);
        }
        
        generateKey(str, literalList, hashIndexPage[useIndexName]);
        cout<<"Key Generated: "<<IndexUtil::keyToString(str, &indexHeader);
        bTree.search(str, arrOperatorType, arrRid);
        cout<<"Matching Records: ";
        for(i=0; i<arrRid.size(); i++){
            cout<<arrRid[i].toString()<<endl;
        }
        //int foundPageNumber = bTree.search(str, arrRid);
        //cout<<"Key found at: "<<foundPageNumber<<endl;
        cout << "Search Complete!!!" << endl;
    } else if (tokens.size() == 3 && tokens[0].compare("SEARCH") == 0 &&
            (tokens[1].compare("=") == 0 || tokens[1].compare("<") == 0 ||
            tokens[1].compare("<=") == 0 || tokens[1].compare(">") == 0 ||
            tokens[1].compare(">=") == 0)) {
        if (useIndexName.compare("") == 0) {
            cout << "Please Use an index to perform this operation!!!";
            return FAILURE;
        }
        BPlusTree bTree(hashIndexPage[useIndexName]);
        int key = atoi(tokens[2].c_str());
        char str[10];
        memcpy(str, &key, sizeof (int));
        vector<RIDStruct> arrRid;

        if (tokens[1].compare("=") == 0)
            bTree.search(str, EQUAL_TO, arrRid);
        else if (tokens[1].compare("<") == 0)
            bTree.search(str, LESS_THAN, arrRid);
        else if (tokens[1].compare("<=") == 0)
            bTree.search(str, LESS_THAN_OR_EQUAL_TO, arrRid);
        else if (tokens[1].compare(">") == 0)
            bTree.search(str, GREATER_THAN, arrRid);
        else if (tokens[1].compare(">=") == 0)
            bTree.search(str, GREATER_THAN_OR_EQUAL_TO, arrRid);
        //int foundPageNumber = bTree.search(str, arrRid);
        //cout<<"Key found at: "<<foundPageNumber<<endl;
        cout << "Search Complete!!!" << endl;
    } else if (tokens.size() == 3 && tokens[0].compare("DISPLAY") == 0 && tokens[1].compare("NODE") == 0) {
        if (useIndexName.compare("") == 0) {
            cout << "Please Use an index to perform this operation!!!";
            return FAILURE;
        }

        IndexHeaderPage indexPage(hashIndexPage[useIndexName]);
        int pageNumber = atoi(tokens[2].c_str());
        if (Page::getPageType(pageNumber) == TYPE_INDEX_PAGE) {
            BPlusTreeNode bNode(&indexPage, pageNumber);
            cout << bNode.toString();
        } else {
            BPlusTreeLeafNode bLeaf(&indexPage, pageNumber);
            cout << bLeaf.toString();
        }
    } else if (tokens.size() == 3 && tokens[0].compare("DISPLAY") == 0 && tokens[1].compare("LEAF") == 0) {
        if (useIndexName.compare("") == 0) {
            cout << "Please Use an index to perform this operation!!!";
            return FAILURE;
        }
        IndexHeaderPage indexPage(hashIndexPage[useIndexName]);
        int pageNumber = atoi(tokens[2].c_str());
        BPlusTreeLeafNode bLeaf(&indexPage, pageNumber);
        cout << bLeaf.toString();
    } else if (tokens.size() == 1 && tokens[0].compare("LIST") == 0) {
        map<string, int>::iterator itr = hashIndexPage.begin();
        if (itr == hashIndexPage.end()) {
            cout << "No Indices Available!!!" << endl;
            return SUCCESS;
        }
        cout << "List of indices available: " << endl;
        while (itr != hashIndexPage.end()) {
            cout << "-> " << itr->first << endl;
            itr++;
        }
    } else if (tokens.size() == 2 && tokens[0].compare("DELETE") == 0) {
        BPlusTree bTree(hashIndexPage[useIndexName]);
        RIDStruct rid;
        int n = atoi(tokens[1].c_str());
        rid.pageNumber = n; // dummy values
        rid.slotNumber = n;
        char str[10];
        memcpy(str, &n, sizeof (int));
        bTree.remove(str, rid);
        cout << "Deletion Complete!!!" << endl;
    } else {
        cout << "Command not Found!!!" << endl;
    }
    return SUCCESS;
}
